using System.Collections.Generic;
using System.Diagnostics;
using HTCollections.Unsafe;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace HTCollections.Examples.UnsafeCollections
{
    public class UnsafeCollectionsExample : MonoBehaviour
    {
        Unsafe.UnsafeList<int> mUnsafeLst;
        Unsafe.UnsafeHashMap<int, int> mUnsafeHashMap;

        void Start()
        {
            mUnsafeLst = Unsafe.UnsafeList<int>.Create();
            mUnsafeHashMap = new Unsafe.UnsafeHashMap<int, int>(6);
        }

        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Q)) //UnsafeList
            {
                //添加元素
                mUnsafeLst.Add(1);
                mUnsafeLst.Add(3);
                mUnsafeLst.Add(5);
                mUnsafeLst.Add(7);
                Debug.Log($"UnsafeList capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                foreach (var item in mUnsafeLst)
                {
                    Debug.Log($"UnsafeList : {item}");
                }

                //列表扩容 (Expend)
                mUnsafeLst.Add(9);
                Debug.Log($"UnsafeList capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                foreach (var item in mUnsafeLst)
                {
                    Debug.Log($"UnsafeList : {item}");
                }

                var lst2 = Unsafe.UnsafeList<int>.Create(4);
                lst2.Add(11);
                lst2.Add(13);

                mUnsafeLst.AddRange(lst2);
                lst2.Dispose();
                Debug.Log($"UnsafeList capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                foreach (var item in mUnsafeLst)
                {
                    Debug.Log($"UnsafeList : {item}");
                }

                //删除元素
                mUnsafeLst.Remove(6);
                Debug.Log($"UnsafeList after remove 6 capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                mUnsafeLst.Remove(5);
                Debug.Log($"UnsafeList after remove 5 capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                foreach (var item in mUnsafeLst)
                {
                    Debug.Log($"UnsafeList : {item}");
                }

                //引用修改元素
                mUnsafeLst.GetElementRef(0) = mUnsafeLst[1];
                for (int i = 0; i < mUnsafeLst.Length; i++)
                {
                    Debug.Log($"引用修改元素后 UnsafeList : {mUnsafeLst[i]}");
                }

                //列表转数组
                var arr = mUnsafeLst.ToArray();
                foreach (var item in arr)
                {
                    Debug.Log($"UnsafeArray : {item}");
                }

                //清空列表
                mUnsafeLst.Clear();
                Debug.Log($"UnsafeList after clear capacity : {mUnsafeLst.Capacity}, count : {mUnsafeLst.Length}");
                foreach (var item in mUnsafeLst)
                {
                    Debug.Log($"UnsafeList : {item}");
                }

                Debug.Log($"UnsafeArray after list clear count : {arr.Length}");
                for (int i = 0; i < arr.Length; i++)
                {
                    Debug.Log($"UnsafeArray after list clear : {arr[i]}");
                }

                mUnsafeLst.Dispose();
                arr.Dispose();
            }

            if (Input.GetKeyDown(KeyCode.W)) //UnsafeHashMap
            {
                //添加键值对
                mUnsafeHashMap.Add(101, 10);
                mUnsafeHashMap.Add(201, 20);
                mUnsafeHashMap.Add(301, 30);
                mUnsafeHashMap.Add(401, 40);
                Debug.Log($"UnsafeHashMap capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }

                //哈希表扩容 (Expend)
                mUnsafeHashMap.Add(501, 50);
                Debug.Log($"UnsafeHashMap capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }

                //获取
                if (mUnsafeHashMap.TryGetValue(202, out var valueB2))
                    Debug.Log($"UnsafeHashMap TryGetValue 202 : {valueB2}");
                if (mUnsafeHashMap.TryGetValue(201, out var valueB1))
                    Debug.Log($"UnsafeHashMap TryGetValue 201 : {valueB1}");

                //查询
                Debug.Log($"UnsafeHashMap {(mUnsafeHashMap.ContainsKey(401) ? "包含" : "不包含")} 401");
                Debug.Log($"UnsafeHashMap {(mUnsafeHashMap.ContainsKey(402) ? "包含" : "不包含")} 402");

                //删除键值对
                mUnsafeHashMap.Remove(302);
                Debug.Log(
                    $"UnsafeHashMap after remove 302 capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                mUnsafeHashMap.Remove(301);
                Debug.Log(
                    $"UnsafeHashMap after remove 301 capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }

                mUnsafeHashMap.Remove(401);
                mUnsafeHashMap.Remove(101);
                Debug.Log(
                    $"UnsafeHashMap after remove 401 101 capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }


                Unsafe.UnsafeHashMap<int, int> addMap = new Unsafe.UnsafeHashMap<int, int>(4);
                addMap.Add(102, 12);
                addMap.Add(202, 22);
                addMap.Add(302, 32);
                addMap.Add(402, 42);
                addMap.Add(103, 13);
                addMap.Add(203, 23);
                addMap.Add(303, 33);
                addMap.Add(403, 43);
                addMap.Add(104, 14);
                addMap.Add(204, 24);
                addMap.Add(304, 34);
                addMap.Add(404, 44);
                addMap.Add(105, 15);
                addMap.Add(205, 25);
                addMap.Add(305, 35);
                addMap.Add(405, 45);
                addMap.Add(106, 16);
                addMap.Add(206, 26);
                addMap.Add(306, 36);
                addMap.Add(406, 46);
                addMap.Add(107, 17);
                addMap.Add(207, 27);
                addMap.Add(307, 37);
                addMap.Add(407, 47);
                mUnsafeHashMap.AddRange(addMap);
                Debug.Log(
                    $"UnsafeHashMap after add 102 202 302 402 capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }

                //清空哈希表
                mUnsafeHashMap.Clear();
                Debug.Log(
                    $"UnsafeHashMap after clear capacity : {mUnsafeHashMap.Capacity}, count : {mUnsafeHashMap.Length}");
                foreach (var item in mUnsafeHashMap)
                {
                    Debug.Log($"UnsafeHashMap : {item.Key}, {item.Value}");
                }
            }

            if (Input.GetKeyDown(KeyCode.E)) //UnsafeArray
            {
                unsafe
                {
                    int size = 5;
                    UnsafeArray<int> arr = new UnsafeArray<int>(size);
                    for (int i = 0; i < size; i++)
                    {
                        int v = arr[i];
                        Debug.Log($"before1 : {v}");
                    }

                    UnsafeUtil.Clear(arr.Ptr, size);
                    for (int i = 0; i < size; i++)
                    {
                        int v = arr[i];
                        Debug.Log($"after1 : {v}");
                    }

                    int* point = UnsafeUtil.Malloc<int>(size);
                    for (int i = 0; i < size; i++)
                    {
                        int v = point[i];
                        Debug.Log($"before2 : {v}");
                    }

                    UnsafeUtil.Clear(point, size);
                    for (int i = 0; i < size; i++)
                    {
                        int v = point[i];
                        Debug.Log($"after2 : {v}");
                    }

                    UnsafeUtil.Free(point);
                    arr.Dispose();
                }
            }

            if (Input.GetKeyDown(KeyCode.R)) //字节对齐
            {
                //字节对齐
                /*
                 * 32位系统下结构体成员变量大小：min(max(成员大小), 8)
                 * 64位系统下结构体成员变量大小：min(max(成员大小), 16)
                 */
                unsafe
                {
                    //按8(long)对齐
                    Debug.Log($"size1 : {sizeof(TestObj1)}"); //24
                    //按8(long)对齐
                    Debug.Log($"size2 : {sizeof(TestObj2)}"); //16
                }
            }

            if (Input.GetKeyDown(KeyCode.T)) //get ptr
            {
                unsafe
                {
                    int size = 5;
                    UnsafeArray<TestObj2> arr = new UnsafeArray<TestObj2>(size);
                    for (int i = 0; i < size; i++)
                    {
                        Debug.Log($"before clear : {arr[i]}");
                    }

                    UnsafeUtil.Clear(arr.Ptr, size);
                    for (int i = 0; i < size; i++)
                    {
                        Debug.Log($"after clear : {arr[i]}");
                    }

                    arr[2] = new TestObj2()
                    {
                        a1 = 1,
                        a2 = true,
                        a3 = 0,
                        a4 = 4,
                        a5 = 5
                    };
                    for (int i = 0; i < size; i++)
                    {
                        Debug.Log($"set1 : {arr[i]}");
                    }

                    TestObj2* point = arr.GetElementPtr(2);
                    point->a1 = 100;
                    point->a2 = false;
                    point->a3 = 1;
                    point->a4 = 400;
                    point->a5 = 500;
                    for (int i = 0; i < size; i++)
                    {
                        Debug.Log($"set2 : {arr[i]}");
                    }

                    UnsafeUtil.Clear(arr.Ptr, size);
                    for (int i = 0; i < size; i++)
                    {
                        Debug.Log($"after clear : {arr[i]}");
                    }

                    arr.Dispose();
                }
            }

            if (Input.GetKeyDown(KeyCode.Y)) //sizeof
            {
                int count = 1000000;
                unsafe
                {
                    Stopwatch sw1 = new Stopwatch();
                    sw1.Start();
                    for (int i = 0; i < count; i++)
                    {
                        int size = sizeof(TestObj2);
                    }

                    sw1.Stop();
                    Debug.Log($"sizeof {count} times : {sw1.ElapsedMilliseconds}ms");
                }
            }

            if (Input.GetKeyDown(KeyCode.U)) //get hash map ptr
            {
                unsafe
                {
                    int size = 5;
                    Unsafe.UnsafeHashMap<int, TestObj2> hashMap = new Unsafe.UnsafeHashMap<int, TestObj2>(size);
                    hashMap.Add(2, new TestObj2()
                    {
                        a1 = 1,
                        a2 = true,
                        a3 = 0,
                        a4 = 4,
                        a5 = 5,
                    });
                    hashMap.Add(7, new TestObj2()
                    {
                        a1 = 7,
                        a2 = true,
                        a3 = 0,
                        a4 = 10,
                        a5 = 11,
                    });
                    foreach (var item in hashMap)
                    {
                        Debug.Log($"{item.Key}, {item.Value}");
                    }

                    TestObj2* point = hashMap.GetElementPtr(2);
                    point->a1 = 100;
                    point->a2 = false;
                    point->a3 = 1;
                    point->a4 = 400;
                    point->a5 = 500;
                    foreach (var item in hashMap)
                    {
                        Debug.Log($"change by ptr : {item.Key}, {item.Value}");
                    }

                    hashMap.Clear();
                    foreach (var item in hashMap)
                    {
                        Debug.Log($"after clear : {item.Key}, {item.Value}");
                    }

                    hashMap.Dispose();
                }
            }
            
            if (Input.GetKeyDown(KeyCode.I)) //优先级队列（小顶堆）
            {
                /*
                UnsafeIntPriorityQueue priorityQueue = new UnsafeIntPriorityQueue(4, false);
                priorityQueue.Enqueue(4);
                priorityQueue.Enqueue(1);
                priorityQueue.Enqueue(9);
                priorityQueue.Enqueue(7);
                priorityQueue.Enqueue(2);
                Debug.Log($"peek : {priorityQueue.Peek()}"); //1
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //1
                Debug.Log($"peek : {priorityQueue.Peek()}"); //2
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //2
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //4
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //7
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //9
                */

                
                UnsafePriorityQueue<int> priorityQueue = new UnsafePriorityQueue<int>(4, false);
                priorityQueue.Enqueue(101, 4);
                priorityQueue.Enqueue(201, 1);
                priorityQueue.Enqueue(301, 9);
                priorityQueue.Enqueue(401, 7);
                priorityQueue.Enqueue(501, 2);
                Debug.Log($"peek : {priorityQueue.Peek()}"); //201
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //201
                Debug.Log($"peek : {priorityQueue.Peek()}"); //501
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //501
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //101
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //401
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //301
                
            }

            if (Input.GetKeyDown(KeyCode.O)) //优先级队列（大顶堆）
            {
                /*
                UnsafeIntPriorityQueue priorityQueue = new UnsafeIntPriorityQueue(4, true);
                priorityQueue.Enqueue(4);
                priorityQueue.Enqueue(1);
                priorityQueue.Enqueue(9);
                priorityQueue.Enqueue(7);
                priorityQueue.Enqueue(2);
                Debug.Log($"peek : {priorityQueue.Peek()}"); //9
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //9
                Debug.Log($"peek : {priorityQueue.Peek()}"); //7
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //7
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //4
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //2
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //1
                */

                
                UnsafePriorityQueue<int> priorityQueue = new UnsafePriorityQueue<int>(4, true);
                priorityQueue.Enqueue(101, 4);
                priorityQueue.Enqueue(201, 1);
                priorityQueue.Enqueue(301, 9);
                priorityQueue.Enqueue(401, 7);
                priorityQueue.Enqueue(501, 2);
                Debug.Log($"peek : {priorityQueue.Peek()}"); //301
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //301
                Debug.Log($"peek : {priorityQueue.Peek()}"); //401
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //401
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //101
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //501
                Debug.Log($"dequeue : {priorityQueue.Dequeue()}"); //201
                
            }

            if (Input.GetKeyDown(KeyCode.A)) //性能测试 List
            {
                /*
                 * custom list : 4.05ms
                 * unity list : 4.85ms
                 * native list : 14.29ms
                 * cs list : 5.09ms
                 */
                int loopCount = 10000;
                int capacity = 16;
                var customLst = new Unsafe.UnsafeList<int>(capacity, Allocator.Persistent);
                var unityLst = new Unity.Collections.LowLevel.Unsafe.UnsafeList<int>(capacity, Allocator.Persistent);
                var nativeLst = new NativeList<int>(capacity, Allocator.Persistent);
                var csLst = new List<int>(capacity);
                
                Stopwatch sw_lst1 = new Stopwatch();
                sw_lst1.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    customLst.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        customLst.Add(j + i);
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        customLst.Remove(j + i);
                    }
                }
                sw_lst1.Stop();
                Debug.Log($"customLst : {sw_lst1.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst2 = new Stopwatch();
                sw_lst2.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    unityLst.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        unityLst.Add(j + i);
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        unityLst.RemoveAt(unityLst.IndexOf(j + i));
                    }
                }
                sw_lst2.Stop();
                Debug.Log($"unityLst : {sw_lst2.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst3 = new Stopwatch();
                sw_lst3.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    nativeLst.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        nativeLst.Add(j + i);
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        nativeLst.RemoveAt(nativeLst.IndexOf(j + i));
                    }
                }
                sw_lst3.Stop();
                Debug.Log($"nativeLst : {sw_lst3.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst4 = new Stopwatch();
                sw_lst4.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    csLst.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        csLst.Add(j + i);
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        csLst.Remove(j + i);
                    }
                }
                sw_lst4.Stop();
                Debug.Log($"csLst : {sw_lst4.Elapsed.TotalMilliseconds}ms");
                
                customLst.Dispose();
                unityLst.Dispose();
                nativeLst.Dispose();
            }
            
            if (Input.GetKeyDown(KeyCode.S)) //性能测试 HashMap
            {
                /*
                 * custom hashMap : 140ms
                 * unity hashMap : 64ms
                 * native hashMap : 105ms
                 * cs hashMap : 45ms
                 */
                int loopCount = 10000;
                int capacity = 16;
                var customHashMap = new Unsafe.UnsafeHashMap<int, int>(capacity, Allocator.Persistent);
                var unityHashMap = new Unity.Collections.LowLevel.Unsafe.UnsafeHashMap<int, int>(capacity, Allocator.Persistent);
                var nativeHashMap = new NativeHashMap<int, int>(capacity, Allocator.Persistent);
                var csHashMap = new Dictionary<int, int>(capacity);
                
                Stopwatch sw_lst1 = new Stopwatch();
                sw_lst1.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    customHashMap.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        customHashMap.Add(j * 37 + i * 13, j * j + i * i + j * i);
                    }
                    for (int j = capacity / 2; j < capacity * 2; j++)
                    {
                        if (customHashMap.ContainsKey(j * 37 + i * 13))
                            customHashMap[j * 37 + i * 13] = 712;
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        customHashMap.Remove(j * 37 + i * 13);
                    }
                }
                sw_lst1.Stop();
                Debug.Log($"customHashMap : {sw_lst1.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst2 = new Stopwatch();
                sw_lst2.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    unityHashMap.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        unityHashMap.Add(j * 37 + i * 13, j * j + i * i + j * i);
                    }
                    for (int j = capacity / 2; j < capacity * 2; j++)
                    {
                        if (unityHashMap.ContainsKey(j * 37 + i * 13))
                            unityHashMap[j * 37 + i * 13] = 712;
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        unityHashMap.Remove(j * 37 + i * 13);
                    }
                }
                sw_lst2.Stop();
                Debug.Log($"unityHashMap : {sw_lst2.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst3 = new Stopwatch();
                sw_lst3.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    nativeHashMap.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        nativeHashMap.Add(j * 37 + i * 13, j * j + i * i + j * i);
                    }
                    for (int j = capacity / 2; j < capacity * 2; j++)
                    {
                        if (nativeHashMap.ContainsKey(j * 37 + i * 13))
                            nativeHashMap[j * 37 + i * 13] = 712;
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        nativeHashMap.Remove(j * 37 + i * 13);
                    }
                }
                sw_lst3.Stop();
                Debug.Log($"nativeHashMap : {sw_lst3.Elapsed.TotalMilliseconds}ms");
                
                Stopwatch sw_lst4 = new Stopwatch();
                sw_lst4.Start();
                for (int i = 0; i < loopCount; i++)
                {
                    csHashMap.Clear();
                    for (int j = 0; j < capacity; j++)
                    {
                        csHashMap.Add(j * 37 + i * 13, j * j + i * i + j * i);
                    }
                    for (int j = capacity / 2; j < capacity * 2; j++)
                    {
                        if (csHashMap.ContainsKey(j * 37 + i * 13))
                            csHashMap[j * 37 + i * 13] = 712;
                    }
                    for (int j = 0; j < capacity / 2; j++)
                    {
                        csHashMap.Remove(j * 37 + i * 13);
                    }
                }
                sw_lst4.Stop();
                Debug.Log($"csHashMap : {sw_lst4.Elapsed.TotalMilliseconds}ms");
                
                customHashMap.Dispose();
                unityHashMap.Dispose();
                nativeHashMap.Dispose();
            }
        }

        struct TestObj1
        {
            public int a1; //0~3(4)
            public bool a2; //4(1)
            public short a3; //6~7(2)
            public byte a4; //8(1)
            public long a5; //16~23(8)
        }

        struct TestObj2
        {
            public int a1; //0~3(4)
            public bool a2; //4(1)
            public byte a3; //5(1)
            public short a4; //6~7(2)
            public long a5; //8~15(8)

            public override string ToString()
            {
                return $"{a1}, {a2}, {a3}, {a4}, {a5}";
            }
        }
    }
}